﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using TGJ_GGA;

namespace BAGA
{
    class Program
    {
        private static StreamWriter sw = null;
        private static double bestfitnesssofar = double.MaxValue;

        private static float[,] data = new float[10, 2];

        private static Random rnd = new Random();

        static void Main(string[] args)
        {
            GAInit gainit = new GAInit();
            gainit.numberOfGenerations = int.MaxValue;
            gainit.populationSize = 25;
            gainit.geneSizeInBytes = 20;
            gainit.elitsm = true;
            gainit.aimForHighFitness = false;
            gainit.keep = 25;
            gainit.allowClones = false;

            data[0, 0] = 0; data[0, 1] = 0;
            data[1, 0] = 1; data[1, 1] = 2;
            data[2, 0] = 2; data[2, 1] = 6;
            data[3, 0] = 3; data[3, 1] = 12;
            data[4, 0] = 4; data[4, 1] = 20;
            data[5, 0] = 5; data[5, 1] = 30;
            data[6, 0] = 6; data[6, 1] = 42;
            data[7, 0] = 7; data[7, 1] = 56;
            data[8, 0] = 8; data[8, 1] = 72;
            data[9, 0] = 9; data[9, 1] = 90;

            sw = new StreamWriter("data x10.dat", false);

            ByteArrayGA BAGA = new ByteArrayGA(gainit, Fitit, ForeachGeneration);

            BAGA.manipulatorHandler.add(BAGA.ByteCrossover, 0.85, "BitToggle");
            //BAGA.manipulatorHandler.add(BAGA.ByteToggle, 0.015, "BitToggle");
            //BAGA.manipulatorHandler.add(BAGA.Substitution, 0.015, "Substitution");
            //BAGA.manipulatorHandler.add(BAGA.ByteSubSubstitution, 0.01, "BitSubSubstitution");
            BAGA.manipulatorHandler.add(BAGA.ByteSwap, 0.50, "swap");
            BAGA.manipulatorHandler.add(inc, 0.32, "dec first");
            BAGA.manipulatorHandler.add(dec, 0.32, "inc first");
            BAGA.manipulatorHandler.add(downPercent, 0.05, "dec first");
            BAGA.manipulatorHandler.add(upPercent, 0.05, "inc first");

            Gene result = BAGA.Run();
            sw.Flush();
            sw.Close();
        }

        //4 bytes
        public static Gene inc(Gene g)
        {
            int startIndex = rnd.Next(g.GeneData.Length / sizeof(byte));

            Gene tempGene = ByteArrayGA.CopyGene(g);
            int i = startIndex * sizeof(byte);
            byte data = ByteArrayGA.ReadByte(ref i, tempGene.GeneData);
            data++;
            byte[] tempdata = BitConverter.GetBytes(data);
            for (byte j = 0; j < 1; j++)
            {
                tempGene.GeneData[j + startIndex * (sizeof(byte))] = tempdata[j];
            }
            return tempGene;
        }

        public static Gene dec(Gene g)
        {
            int startIndex = rnd.Next(g.GeneData.Length / sizeof(byte));

            Gene tempGene = ByteArrayGA.CopyGene(g);
            int i = startIndex * sizeof(byte);
            byte data = ByteArrayGA.ReadByte(ref i, tempGene.GeneData);
            data--;
            byte[] tempdata = BitConverter.GetBytes(data);
            for (byte j = 0; j < 1; j++)
            {
                tempGene.GeneData[j + startIndex * (sizeof(byte))] = tempdata[j];
            }
            return tempGene;
        }
        //4 bytes
        public static Gene upPercent(Gene g)
        {
            int startIndex = rnd.Next(g.GeneData.Length / sizeof(byte));

            Gene tempGene = ByteArrayGA.CopyGene(g);
            int i = startIndex * sizeof(byte);
            byte data = ByteArrayGA.ReadByte(ref i, tempGene.GeneData);
            data = (byte)(data * 2);
            byte[] tempdata = BitConverter.GetBytes(data);
            for (byte j = 0; j < 1; j++)
            {
                tempGene.GeneData[j + startIndex * (sizeof(byte))] = tempdata[j];
            }
            return tempGene;
        }

        public static Gene downPercent(Gene g)
        {
            int startIndex = rnd.Next(g.GeneData.Length / sizeof(byte));

            Gene tempGene = ByteArrayGA.CopyGene(g);
            int i = startIndex * sizeof(byte);
            byte data = ByteArrayGA.ReadByte(ref i, tempGene.GeneData);
            data = (byte)(data / 2);
            byte[] tempdata = BitConverter.GetBytes(data);
            for (byte j = 0; j < 1; j++)
            {
                tempGene.GeneData[j + startIndex * (sizeof(byte))] = tempdata[j];
            }
            return tempGene;
        }

        //return false to stop run.
        public static void Fitit(Gene g)
        {
            double SStot = 0;
            double SSreg = 0;
            double SSerr = 0;
            double yStreg = 0;

            for (byte j = 1; j < 10; j++)
            {
                yStreg += data[j, 1];
            }
            yStreg /= 10;

            int i = 0;
            byte c1 = ByteArrayGA.ReadByte(ref i, g.GeneData);
            byte c2 = ByteArrayGA.ReadByte(ref i, g.GeneData);
           
            g.fitness = 0;

            for (byte j = 1; j < 10; j++)
            {
                float x = data[j, 0];
                float y = data[j, 1];
                float yi = 
                    c2 * x *x + 
                    c1 * x;

                SStot += Math.Pow(yi - yStreg, 2);
                SSerr += Math.Pow(yi - y,2);
                g.fitness = SSerr;//+= Math.Abs(Math.Log(yi) / Math.Log (y));// SSerr;
            }
            //g.fitness = 1.0 - (SSerr / SStot);//Math.Pow(res - y, 2);
            
        }

        public static void ForeachGeneration(int i, List<Gene> pop)
        {
            if (double.IsInfinity(pop[0].fitness))
            {
                Console.ReadKey();
            }
            int j = 0;
            byte c1 = ByteArrayGA.ReadByte(ref j, pop[0].GeneData);
            byte c2 = ByteArrayGA.ReadByte(ref j, pop[0].GeneData);
            
            if (pop[0].fitness < bestfitnesssofar)
            {
                bestfitnesssofar = pop[0].fitness;
                sw.WriteLine(i.ToString() +
                    "\t" + pop[0].fitness.ToString("0.000000") +
                    "\t" + c1 +
                    "\t" + c2);
                sw.Flush();
                Console.WriteLine(i.ToString() + "\t" + pop[0].fitness.ToString("0.00000000") +
                    " | " + c1 +
                    " | " + c2
                    );
            }
            if (pop[0].fitness == 0)
            {
                Console.WriteLine("DONE!!");
                Console.ReadKey();
                Environment.Exit(0);
            }
        }
    }
}
